﻿#pragma once

#include <functional>
#include <memory>
#include <string>
#include <unordered_map>

#include "../util/box_std_allocator.hh"
#include "script_debug_server.hh"

namespace Json {
class Value;
}

namespace kratos {
namespace ws {
class WebSocketServer;
class WebSocketChannel;
enum class WebSocketEvent;
} // namespace ws
} // namespace kratos

#define DEBUG_COMMAND_HANDLE_DECL(name)                                        \
  auto handle_##name(Json::Value &root, Json::Value &result,                   \
                     WebSocketChannelPtr channel)                              \
      ->void

#define DEBUG_COMMAND_HANDLE_IMPL(name)                                        \
  auto kratos::service::DebugServerImpl::handle_##name(                        \
      Json::Value &root, Json::Value &result,                                  \
      kratos::ws::WebSocketChannelPtr channel)                                 \
      ->void

#define DEGUB_COMMAND_REGISTRY(name)                                           \
  register_command(#name, &DebugServerImpl::handle_##name)

namespace kratos {
namespace service {

class ServiceBox;
class ScriptDebugger;
class ScriptService;

using WebSocketServerPtr = std::shared_ptr<kratos::ws::WebSocketServer>;
using WebSocketChannelPtr = std::shared_ptr<kratos::ws::WebSocketChannel>;

class DebugServerImpl : public ScriptDebugServer {
  using CommandCallback =
      std::function<void(Json::Value &, Json::Value &, WebSocketChannelPtr)>;
  using CommandMap =
      kratos::service::PoolUnorederedMap<std::string, CommandCallback>;
  struct ScriptModule {
    ScriptDebugger *debugger{nullptr};
    ScriptService *service{nullptr};
  };
  using DebuggerMap =
      kratos::service::PoolUnorederedMap<std::string, ScriptModule>;
  using ClassMethod = void (DebugServerImpl::*)(Json::Value &, Json::Value &,
                                                WebSocketChannelPtr);
  using DebuggerChannel =
      kratos::service::PoolUnorederedMap<std::string, std::uint64_t>;
  kratos::service::ServiceBox *box_{nullptr}; ///< 服务容器
  WebSocketServerPtr websocket_server_ptr_;   ///< Websocket服务器
  DebuggerMap debugger_map_;                  ///< 虚拟机调试器表
  CommandMap command_map_;                    ///< 命令表
  DebuggerChannel debugger_channel_; ///< 管道与调试器对应关系

public:
  /**
   * @brief 构造
   * @param box 服务容器
   */
  DebugServerImpl(kratos::service::ServiceBox *box);
  /**
   * @brief 析构
   */
  virtual ~DebugServerImpl();
  virtual auto start(const std::string &ip, int port) -> bool override;
  virtual auto stop() -> bool override;
  virtual auto update() -> void override;
  virtual auto enable_machine(const std::string &name, ScriptService *service,
                              kratos::service::ScriptDebugger *debugger)
      -> void override;
  virtual auto disable_machine(const std::string &name) -> void override;

private:
  /**
   * @brief 注册命令
   * @return
   */
  auto register_all_command() -> void;
  /**
   * @brief 注册一个成员函数
   * @param command 命令名
   * @param method 方法
   * @return
   */
  auto register_command(const std::string &command, ClassMethod method) -> void;
  /**
   * @brief 注册一个命令处理器
   * @param command 命令名
   * @param cb 回调
   * @return
   */
  auto register_command(const std::string &command, CommandCallback cb) -> void;
  /**
   * @brief Websocket服务器回调
   * @param e 事件
   * @param channel 管道
   * @return
   */
  auto server_callback(kratos::ws::WebSocketEvent e,
                       WebSocketChannelPtr channel) -> void;
  /**
   * @brief 处理客户端事件
   * @param channel 管道
   * @return
   */
  auto on_client_command(WebSocketChannelPtr channel) -> void;
  /**
   * @brief 获取调试器
   * @param command 当前需要执行的命令
   * @param root 客户端发送来的命令
   * @param result 返回给客户端的命令
   * @param channel 客户端管道
   * @return 调试器
   */
  auto get_debugger(const std::string &command, const Json::Value &root,
                    Json::Value &result, WebSocketChannelPtr channel)
      -> ScriptDebugger *;
  /**
   * @brief 清理管道相关的资源
   * @param channel 管道
   * @return
   */
  auto channel_post_close(WebSocketChannelPtr channel) -> void;
  /**
   * @brief 调试器回调
   * @param
   * @return
   */
  auto debugger_callback(const ScriptDebugger &debugger) -> void;

private:
  //
  // 调试器命令处理器
  //

  DEBUG_COMMAND_HANDLE_DECL(get_machine_list);
  DEBUG_COMMAND_HANDLE_DECL(attach);
  DEBUG_COMMAND_HANDLE_DECL(detach);
  DEBUG_COMMAND_HANDLE_DECL(get_tree);
  DEBUG_COMMAND_HANDLE_DECL(step_in);
  DEBUG_COMMAND_HANDLE_DECL(step_out);
  DEBUG_COMMAND_HANDLE_DECL(step_over);
  DEBUG_COMMAND_HANDLE_DECL(continue);
  DEBUG_COMMAND_HANDLE_DECL(get_breakpoint);
  DEBUG_COMMAND_HANDLE_DECL(add_breakpoint);
  DEBUG_COMMAND_HANDLE_DECL(remove_breakpoint);
  DEBUG_COMMAND_HANDLE_DECL(enable_breakpoint);
  DEBUG_COMMAND_HANDLE_DECL(disable_breakpoint);
  DEBUG_COMMAND_HANDLE_DECL(enable_debugger);
  DEBUG_COMMAND_HANDLE_DECL(disable_debugger);
  DEBUG_COMMAND_HANDLE_DECL(upvalues);
  DEBUG_COMMAND_HANDLE_DECL(locals);
  DEBUG_COMMAND_HANDLE_DECL(list);
  DEBUG_COMMAND_HANDLE_DECL(print);
  DEBUG_COMMAND_HANDLE_DECL(eval);
  DEBUG_COMMAND_HANDLE_DECL(open_file);
  DEBUG_COMMAND_HANDLE_DECL(save_file);
  DEBUG_COMMAND_HANDLE_DECL(add_file);
  DEBUG_COMMAND_HANDLE_DECL(delete_file);
  DEBUG_COMMAND_HANDLE_DECL(add_dir);
  DEBUG_COMMAND_HANDLE_DECL(remove_dir);
  DEBUG_COMMAND_HANDLE_DECL(rename_dir);
  DEBUG_COMMAND_HANDLE_DECL(reload);
  DEBUG_COMMAND_HANDLE_DECL(get_thread);
  DEBUG_COMMAND_HANDLE_DECL(backtrace);
  DEBUG_COMMAND_HANDLE_DECL(frame);
  DEBUG_COMMAND_HANDLE_DECL(restart);
  DEBUG_COMMAND_HANDLE_DECL(get_log);
};

} // namespace service
} // namespace kratos
